﻿/// <reference path="jquery-1.7.1.min.js" />
/// <reference path="OOPager.js" />
/// <reference path="Linq.js" />
/// <reference path="jquery.styling.js" /> this script is used in the grid.Busy() interface
/// last modified: 08-08-2011


var SortDirection = {
    Ascending: "asc",
    Descending: "desc"
};

var DataType = {
    String: "string",
    Number: "number",
    Date: "date"
};

var GridRowSelectMode = {
    Single: "Single",
    Mutiple: "Mutiple"
};

var Column = function () {
    this.Width = 80;
    this.Sortable = true;
    this.Sorted = false;
    this.SortedDirection = SortDirection.Ascending;
    this.MemberName = "";
    this.MemberDataType = DataType.String;
    this.HeaderHtml = "";
    this.HeaderClass = "";
    this.CellClass = "";
    this.CellTemplate = "";

    this.CellFormaterFunction = null;

    this.SetWidth = function (width) {
        this.Width = width;
        return this;
    };
    this.SetSortable = function (sortable) {
        this.Sortable = sortable;
        return this;
    };
    this.SetSorted = function (sorted) {
        this.Sorted = sorted;
        return this;
    };
    this.SetSortedDirection = function (sortedDirection) {
        this.SortedDirection = sortedDirection;
        return this;
    };
    this.SetMember = function (name) {
        this.MemberName = name;
        return this;
    };
    this.SetMemberDataType = function (memberDataType) {
        this.MemberDataType = memberDataType;
        return this;
    };
    this.SetHeaderHtml = function (headerHtml) {
        this.HeaderHtml = headerHtml;
        return this;
    };
    this.SetHeaderClass = function (headerClass) {
        this.HeaderClass = headerClass;
        return this;
    };
    this.SetCellClass = function (cellClass) {
        this.CellClass = cellClass;
        return this;
    };
    this.SetCellTemplate = function (cellTemplate) {
        this.CellTemplate = cellTemplate;
        return this;
    };
    this.SetCellFormaterFunction = function (cellFormaterFunc) {
        this.CellFormaterFunction = cellFormaterFunc;
        return this;
    };
};

var GridRow = function () {
    this.RowElement = null;
    this.RowData = null;
};

var EventArgs = function (data) {
    this.Data = data;
};

var SortingEventArgs = function (column, canceld) {
    this.Column = column;
    this.Canceld = canceld;
};

var PageIndexChangedEventArgs = function (oldIndex, newIndex) {
    this.OldIndex = oldIndex;
    this.NewIndex = newIndex;
};

var PageIndexChangingEventArgs = function (oldIndex, newIndex, canceled) {
    this.OldIndex = oldIndex;
    this.NewIndex = newIndex;
    this.Canceled = canceled;
};

var OOGrid = function (gridId) {
    if (gridId == null || gridId == "") {
        alert("Please specify the div ID for the grid!");
    }

    this.WrapperId = gridId;
    this.Mode = OOGrid.Modes.Ajax;
    this.AjaxUrl = null;
    this.AjaxDataFunction = null;
    this.BodyHeight = 300;
    this.AutoLoad = true;
    this.RowSelectMode = GridRowSelectMode.Single;

    this.Pager = new OOPager("#" + gridId + " .grid-pager", "#" + gridId + " .grid-info");
    this.Pager.RenderPagerOnClick = false;
    this.UpdateTotal = true;
    this.Pageable = true;

    this.Columns = new Array();
    this.Rows = new Array();
    this.TotalRows = 0;
    this.ShowRefresher = true;
    this.EnableRowSelect = true;


};

OOGrid.EventNames = {
    Initialized: "Initialized",
    RowDataBinding: "RowDataBinding",
    RowDataBound: "RowDataBound",
    DataBinding: "DataBinding",
    DataBound: "DataBound",
    RowSelected: "RowSelected",
    RowUnselected: "RowUnselected",
    RowDoubleClicked: "RowDoubleClick",
    ColumnSorting: "ColumnSorting",
    ColumnSorted:"ColumnSorted",
    PageIndexChanging: "PageIndexChanging",
    PageIndexChanged: "PageIndexChanged",
    AjaxLoadingData: "AjaxLoadingData",
    AjaxLoadingDataCompleted: "AjaxLoadingDataCompleted",
    AjaxLoadingDataError: "AjaxLoadingDataError"
};

OOGrid.Modes = {
    Ajax: "Ajax",
    Client: "Client"
};

OOGrid.prototype.Initialize = function () {
    var grid = this;

    var $wrapper = $("#" + grid.WrapperId);
    $wrapper.empty().addClass("oogrid");

    var $header = GridFactory.CreateGridHeader(grid, grid.Columns);
    $header.appendTo($wrapper);

    var $body = GridFactory.CreateGridBody(grid.Columns, grid.BodyHeight);
    $body.appendTo($wrapper);

    var $footer = GridFactory.CreateGridFooter();
    $footer.appendTo($wrapper);

    grid.Pager.OnPageClicked(function (index) {
        grid.PageTo(index);
    });

    if (grid.ShowRefresher) {
        var $refershWrapper = grid.GetRefreshWrapper();
        GridFactory.CreateRefresher($refershWrapper, function () {
            grid.Refresh();
        });
    }
    $(grid).trigger(OOGrid.EventNames.Initialized);
    if (grid.AutoLoad) {
        grid.Refresh();
    }
};

OOGrid.prototype.Refresh = function () {
    var grid = this;
    if (grid.AjaxUrl == null) {
        return;
    }
    var postData = "pageIndex=" + grid.Pager.PageIndex;
    postData += "&pageSize=" + grid.Pager.PageSize;
    postData += "&updateTotal=" + grid.UpdateTotal;

    var sortedCount = 0;
    for (var i = 0; i < grid.Columns.length; i++) {
        var column = grid.Columns[i];
        if (column.Sorted) {
            postData += "&sortedColumns[" + sortedCount + "].MemberPath=" + column.MemberName;
            postData += "&sortedColumns[" + sortedCount + "].IsAcending=" + (column.SortedDirection == SortDirection.Ascending);
            sortedCount++;
        }
    }
    if (grid.AjaxDataFunction != null) {
        postData = grid.AjaxDataFunction(postData);
    }

    var successCallback = null;
    var errorCallback = null;
    if (arguments.length > 0) {
        successCallback = arguments[0];
    }
    if (arguments.length > 1) {
        errorCallback = arguments[1];
    }

    $.ajax({
        type: "post",
        contentType: "application/x-www-form-urlencoded",
        url: grid.AjaxUrl,
        data: postData,
        success: function (result) {
            grid.Columns.Where(function (c) {
                return c.MemberDataType == DataType.Date;
            }).Each(function (col) {
                if (result.Rows != null) {
                    result.Rows.Each(function (row) {
                        var strDate = row[col.MemberName];
                        if (strDate != null && strDate.substr(0, 6) == "/Date(") {
                            strDate = strDate.substring(6, strDate.length - 2);
                            row[col.MemberName] = new Date(strDate * 1);
                        }
                    });
                }
            });


            grid.UpdateTotal = false;
            var event = $.Event(OOGrid.EventNames.AjaxLoadingDataCompleted);
            event.Result = result;
            $(grid).trigger(event);
            grid.Busy(false);

            grid.Rows = result.Rows;
            grid.GetTableBody().empty();
            if (grid.Mode == OOGrid.Modes.Ajax) {
                if (result.UpdateTotal) {
                    grid.Pager.Total = result.Total;
                }
                if (grid.Pageable) {
                    grid.Pager.Render();
                }
                for (var i = 0; i < result.Rows.length; i++) {
                    grid.AppendRow(result.Rows[i], i);
                }
            }
            else {
                grid.PageTo(grid.Pager.PageIndex);
            }
            if (successCallback != null) {
                successCallback();
            }
        },
        beforeSend: function (request) {
            var event = $.Event(OOGrid.EventNames.AjaxLoadingData);
            event.Request = request;
            $(grid).trigger(event);
            grid.Busy(true, request);
        },
        error: function (err) {
            var event = $.Event(OOGrid.EventNames.AjaxLoadingDataError);
            event.Error = err;
            $(grid).trigger(err);
            if (errorCallback != null) {
                errorCallback();
            }
        }
    });
};

OOGrid.prototype.BindData = function (rows) {
    var grid = this;
    grid.Rows = rows;
    grid.PageTo(grid.PageIndex);
};

OOGrid.prototype.PageTo = function (index) {
    var grid = this;
    var eventPageChanging = $.Event(OOGrid.EventNames.PageIndexChanging);
    eventPageChanging.Args = new PageIndexChangingEventArgs(grid.Pager.PageIndex, index, false);
    $(grid).trigger(eventPageChanging);
    if (eventPageChanging.Args.Canceled) {
        return;
    }

    if (grid.Mode == OOGrid.Modes.Client) {
        var oldIndex = grid.Pager.PageIndex;
        grid.Pager.PageTo(index);
        var skip = index * grid.Pager.PageSize;
        var end = skip + grid.Pager.PageSize;
        end = end > grid.Rows.length ? grid.Rows.length : end;
        var $tbody = grid.GetTableBody();
        $tbody.empty();

        $(grid).trigger($.Event(OOGrid.EventNames.DataBinding));
        if (end < skip + 1) {
            var $tr = $("<tr><td colspan='" + grid.Columns.length + "'><div class='row-empty'>No items found.</div></td></tr>");
            $tr.appendTo($tbody);
        } else {
            for (var i = skip; i < end; i++) {
                grid.AppendRow(grid.Rows[i], i);
            }
        }
        $(grid).trigger($.Event(OOGrid.EventNames.DataBound));

        var eventPageChanged = $.Event(OOGrid.EventNames.PageIndexChanged);
        eventPageChanged.Args = new PageIndexChangedEventArgs(oldIndex, index);
        $(grid).trigger(eventPageChanged);
    }
    else {
        grid.Pager.PageIndex = index;
        grid.Refresh();
    }
};

OOGrid.prototype.AppendRow = function (row, i) {
    var grid = this;
    var $tbody = grid.GetTableBody();
    var evenOdd = i % 2 == 0 ? "even" : "odd";
    var $tr = $("<tr id='row_" + i + "' class='row_" + evenOdd + "'></tr>");
    $tr.appendTo($tbody).data("dataItem", row);

    var eventDataBinding = $.Event(OOGrid.EventNames.RowDataBinding);
    eventDataBinding.RowData = row;
    eventDataBinding.Tr = $tr;
    $(grid).trigger(eventDataBinding);

    for (var j = 0; j < grid.Columns.length; j++) {
        var column = grid.Columns[j];
        var $cell = $("<td></td>");
        if (j == grid.Columns.length - 1) {
            $cell.addClass("cell-last");
        }
        $cell.appendTo($tr);
        if (column.CellClass != "") {
            var $wrapper = $("<div class='" + column.CellClass + "'></div>");
            $wrapper.appendTo($cell);
            $cell = $wrapper;
        }
        var value = row[column.MemberName];
        if (column.CellFormaterFunction != null) {
            value = column.CellFormaterFunction(value);
        }
        value = value + "";
        if (column.CellTemplate != "") {
            var reg = new RegExp("##" + column.MemberName + "##", "g");
            value = column.CellTemplate.replace(reg, value);
        }
        $cell.html(value);
    }

    var eventDataBound = $.Event(OOGrid.EventNames.RowDataBound);
    eventDataBound.RowData = row;
    eventDataBound.Tr = $tr;
    $(grid).trigger(eventDataBound);


    $tr.mouseover(function () {
        $(this).addClass("row-mouseover");
    });
    $tr.mouseout(function () {
        $(this).removeClass("row-mouseover");
    });
    if (grid.EnableRowSelect) {
        $tr.click(function () {
            var $currentRow = $(this);
            var rowIndex = $currentRow.attr("id").replace(/row_/, "") * 1;
            if (grid.RowSelectMode == GridRowSelectMode.Single) {
                if (!$currentRow.hasClass("row-selected")) {
                    $tbody.find(".row-selected").removeClass("row-selected");
                    $currentRow.addClass("row-selected");
                    var eventSelected = $.Event(OOGrid.EventNames.RowSelected);
                    eventSelected.RowData = grid.Rows[rowIndex];
                    eventSelected.Tr = $currentRow;
                    $(grid).trigger(eventSelected);
                }
            } else {
                if ($currentRow.hasClass("row-selected")) {
                    $currentRow.removeClass("row-selected");
                    var eventUnSelected = $.Event(OOGrid.EventNames.RowUnselected);
                    eventUnSelected.RowData = grid.Rows[rowIndex];
                    eventUnSelected.Tr = $currentRow;
                    $(grid).trigger(eventUnSelected);
                } else {
                    $currentRow.addClass("row-selected");
                    var eventSelected2 = $.Event(OOGrid.EventNames.RowSelected);
                    eventSelected2.RowData = grid.Rows[rowIndex];
                    eventSelected2.Tr = $currentRow;
                    $(grid).trigger(eventSelected2);
                }
            }

        });
    }
    $tr.dblclick(function () {
        var $currentRow = $(this);
        var eventDoubleClicked = $.Event(OOGrid.EventNames.RowDoubleClicked);
        var rowIndex = $currentRow.attr("id").replace(/row_/, "") * 1;
        eventDoubleClicked.RowData = grid.Rows[rowIndex];
        eventDoubleClicked.Tr = $currentRow;
        $(grid).trigger(eventDoubleClicked);
    });
};

OOGrid.prototype.Sort = function (column) {
    var grid = this;
    var sortingEvent = $.Event(OOGrid.EventNames.ColumnSorting);
    var sortingArgs = new SortingEventArgs(column, false);
    sortingEvent.Data = sortingArgs;
    $(grid).trigger(sortingEvent);
    if (sortingArgs.Canceld) {
        return;
    }

    var $th = null;
    var $header = grid.GetHeader();
    $header.find("th").each(function () {
        if ($(this).data("columnData") == column) {
            $th = $(this);
            return;
        }
    });

    var oldSortDirection = column.SortedDirection;
    var wasSorted = column.Sorted;
    if (column.Sorted) {
        if (column.SortedDirection == SortDirection.Descending) {
            column.SortedDirection = SortDirection.Ascending;
        } else {
            column.SortedDirection = SortDirection.Descending;
        }
    } else {
        column.SortedDirection = SortDirection.Ascending;
    }
    for (var i = 0; i < grid.Columns.length; i++) {
        grid.Columns[i].Sorted = false;
    }
    column.Sorted = true;

    function updateSortedState() {
        $header.find(".sortable").removeClass("sorted-asc").removeClass("sorted-desc");
        if (column.SortedDirection == SortDirection.Ascending) {
            $th.addClass("sorted-asc");
        }
        else {
            $th.addClass("sorted-desc");
        }
        var sortedEvent = $.Event(OOGrid.EventNames.ColumnSorted);
        sortedEvent.Data = column;
        $(grid).trigger(sortedEvent);
    }

    if (grid.Mode == OOGrid.Modes.Client) {
        var sortedData = new Array();
        if (wasSorted) {
            sortedData = grid.Rows.reverse();
        }
        else {
            //todo:
            //GridHelper.SortData(grid.Rows, column.MemberName, column.MemberDataType, column.SortedDirection);
        }
        grid.Rows = sortedData;
        grid.PageTo(grid.PageIndex);

        updateSortedState();
    }
    else {
        grid.Refresh(function () {
            //success
            updateSortedState();
        }, function () {
            //error
            column.SortedDirection = oldSortDirection;
            column.Sorted = wasSorted;
        });
    }
};

OOGrid.prototype.GetSelectedRows = function() {
    var grid = this.GetInstance();
    var rows = new Array();
    grid.GetTableBody().find(".row-selected").each(function() {
        var rowIndex = this.id.replace( /row_/ , "") * 1;
        var row = new GridRow();
        row.RowElement = $(this);
        row.RowData = grid.Rows[rowIndex];
        rows.push(row);
    });
    return rows;
};

OOGrid.prototype.GetHeader = function() {
    return $("#" + this.WrapperId + " .grid-header");
};

OOGrid.prototype.GetTableBody = function() {
    return $("#" + this.WrapperId + " .grid-body tbody");
};

OOGrid.prototype.GetPagerWrapper = function() {
    return $("#" + this.WrapperId + " .grid-pager");
};

OOGrid.prototype.GetInfoWrapper = function() {
    return $("#" + this.WrapperId + " .grid-info");
};

OOGrid.prototype.GetRefreshWrapper = function() {
    return $("#" + this.WrapperId + " .grid-refresher");
};

OOGrid.prototype.OnAjaxLoadingData = function (func) {
    $(this).bind(OOGrid.EventNames.AjaxLoadingData, func);
};
OOGrid.prototype.OnAjaxLoadingDataCompleted = function (func) {
    $(this).bind(OOGrid.EventNames.AjaxLoadingDataCompleted, func);
};
OOGrid.prototype.OnAjaxLoadingDataError = function (func) {
    $(this).bind(OOGrid.EventNames.AjaxLoadingDataError, func);
};
OOGrid.prototype.OnColumnSorted = function (func) {
    $(this).bind(OOGrid.EventNames.ColumnSorted, func);
};
OOGrid.prototype.OnColumnSorting = function (func) {
    $(this).bind(OOGrid.EventNames.ColumnSorting, func);
};
OOGrid.prototype.OnDataBinding = function (func) {
    $(this).bind(OOGrid.EventNames.DataBinding, func);
};
OOGrid.prototype.OnDataBound = function (func) {
    $(this).bind(OOGrid.EventNames.DataBound, func);
};
OOGrid.prototype.OnInitialized = function (func) {
    $(this).bind(OOGrid.EventNames.Initialized, func);
};
OOGrid.prototype.OnPageIndexChanged = function (func) {
    $(this).bind(OOGrid.EventNames.PageIndexChanged, func);
};
OOGrid.prototype.OnPageIndexChanging = function (func) {
    $(this).bind(OOGrid.EventNames.PageIndexChanging, func);
};
OOGrid.prototype.OnRowDataBinding = function (func) {
    $(this).bind(OOGrid.EventNames.RowDataBinding, func);
};
OOGrid.prototype.OnRowDataBound = function (func) {
    $(this).bind(OOGrid.EventNames.RowDataBound, func);
};
OOGrid.prototype.OnRowDoubleClicked = function (func) {
    $(this).bind(OOGrid.EventNames.RowDoubleClicked, func);
};
OOGrid.prototype.OnRowSelected = function (func) {
    $(this).bind(OOGrid.EventNames.RowSelected, func);
};
OOGrid.prototype.OnRowUnselected = function (func) {
    $(this).bind(OOGrid.EventNames.RowUnselected, func);
};


//interfaces

OOGrid.prototype.Busy = function (isBusy, request) {
    var grid = this;
    var $refresher = grid.GetRefreshWrapper().find("a");
    if (isBusy) {
        $refresher.html("Loading...");
    }
    else {
        $refresher.html("Refresh");
    }
};


OOGrid.prototype.FormatDate = function(date) {
    if (date.constructor != Date) {
        return date;
    }
    var year = date.getYear() + 1900;
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var hour = date.getHours();
    var minute = date.getMinutes();

    return month + "/" + day + "/" + year + " " + hour + ":" + minute;
};

/**************************************************************************************************/
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/**************************************************************************************************/


GridFactory = function() {

};

GridFactory.CreateSimpleTable = function(columns) {
    var table = $("<table></table>");
    var group = $("<colgroup></colgroup>");
    group.appendTo(table);
    for (var i = 0; i < columns.length; i++) {
        var col = $("<col>");
        if (columns[i].Width != "auto") {
            col.css("width", columns[i].Width + "px");
        }
        col.appendTo(group);
    }
    var tbody = $("<tbody></tbody>");
    tbody.appendTo(table);
    return table;
};

GridFactory.CreateGridHeader = function (grid, columns) {
    var $header = $("<div class='grid-header'></div>");
    var $headWrapper = $("<div class='grid-header-wrapper'></div>");
    $headWrapper.appendTo($header);

    var $headerTable = GridFactory.CreateSimpleTable(columns);
    $headerTable.appendTo($headWrapper);
    var $headerTableBody = $headerTable.find("tbody");
    var $tr = $("<tr></tr>");
    $tr.appendTo($headerTableBody);

    for (var i = 0; i < columns.length; i++) {
        var column = columns[i];
        var $th = $("<th id='column_" + i + "'></th>");
        $th.data("columnData", column);
        var $thWrapper = $("<div class='oogrid-column'></div>");
        $thWrapper.appendTo($th);
        if (column.Sortable) {
            $th.click(function () {
                var columnIndex = this.id.replace(/column_/, "") * 1;
                grid.Sort(columns[columnIndex]);
            });
        }
        var $wrapper = $thWrapper;
        if (column.HeaderClass != "") {
            var $userClassWrapper = $("<div class='" + column.HeaderClass + "'></div>");
            $userClassWrapper.appendTo($wrapper);
            $wrapper = $userClassWrapper;
        }
        var headerHtml = column.HeaderHtml;
        headerHtml = headerHtml == "" ? column.MemberName : headerHtml;
        $wrapper.html(headerHtml);

        if (column.Sortable) {
            $th.addClass("sortable");
            var $sortWrapper = $("<span class='sort-status'></span>");
            $sortWrapper.appendTo($wrapper);
        }
        $th.appendTo($tr);
    }
    return $header;
};

GridFactory.CreateGridBody = function(columns, height) {
    var body = $("<div class='grid-body'></div>");
    body.css("height", height + "px");
    var bodyTable = GridFactory.CreateSimpleTable(columns);
    bodyTable.appendTo(body);
    return body;
};

GridFactory.CreateGridFooter = function() {
    var $footer = $("<div class='grid-footer'></div>");
    var $refresher = $("<div class='grid-refresher'></div>");
    $refresher.appendTo($footer);

    var $pagerWrapper = $("<div class='grid-pager'></div>");
    $pagerWrapper.appendTo($footer);

    var $info = $("<div class='grid-info'></div>");
    $info.appendTo($footer);

    var $clear = $("<div class='clear'></div>");
    $clear.appendTo($footer);

    return $footer;
};

GridFactory.CreateRefresher = function (wrapper, refreshCallback) {
    var button = $("<a class='link-refresh'>Refresh</a>");
    button.appendTo(wrapper);
    button.click(function () {
        refreshCallback();
    });
}
